/////////////////////////////////////////////////////////////////
// File: main.c
// Description: Entry point for the program, contains high level control
// Developed by Mezzo Forte
// Last edit 3/10/2010 : Complete Renovation
//

#include <stdio.h>
#include "data_types.h"
#include "register_system.h"
#include "register_cpu.h"
#include "rtc.h"
#include "control.h"
#include "i2s.h"
#include "dma.h"
#include "timer.h"
#include "mf_csl_functions.h"
#include "mf_fat32.h"
#include "mfutils.h"
#include "usbstk5505_i2c.h"
#include "lcdi2c.h"
#include "i2cexp.h"
#include "message_strings.h"


#define FINAL_BUFF_SIZE 800
#define DEBOUNCE 10
#define VOLUME_MIN -120
#define VOLUME_MAX 48
#define FILTERLENGTH 100

void InitSystem(void);
void ConfigPort(void);
void SYS_GlobalIntEnable(void);
void SYS_GlobalIntDisable(void);
void BlinkLED(void);
Uint16 GenerateAudioTone(Uint16 currNote, Uint16 noteAmount);
int FileMenu(MF_FAT32_Filesystem *fs);
int settingsMenu();
int MainMenu();
void nowPlayingScr(MF_FileInfo *theFile, int playedNote, int override);

Uint16 State = 0;
Uint16 note =0; 
Int16 PINGPONG =0;
Uint16 triggerLevel = 150;
Int16 dynVolume = -120;
CSL_SarHandleObj sar_handle;
Uint16 adc_value;
Int16 current_cluster=0;


unsigned long finalBuff[FINAL_BUFF_SIZE];

extern Uint16 fTimer;
extern Uint16 Flag_RTC;
extern Uint16 Conunt_RTC;
extern Uint16 halfSecCnt;
extern void AIC3254_init(void);


int main(void)
{
	int contentsIndex;
	Uint16 notePointer = 0;
	MF_FAT32_Filesystem fs;
	MF_FAT32_Filesystem* fsp;
	Uint16 currNote =1;
	fsp = &fs;
	
	
	
	
	// BEGIN INIT ////////////////////////////
    InitSystem();
    ConfigPort();

    SYS_GlobalIntEnable();
    
    
    /// Timer Init. /////////////////////
    IER0 = 0x0010;      // enable timer int      
    IER1 = 0x0004;      // enable RTC int

    Timer0Init();
    
	reset_RTC();
	
	enable_rtc_second_int();
	
	/// END Timer Init. ////////////////////
	
	AIC3254_init(); 
	AIC3204_rset( 60, 2);
	
	set_i2s0_slave();
    enable_i2s0();
    
	StartTimer0();
	
	GPIO_dir(21, GPIO_IN );
	USBSTK5505_I2C_init();
	pullUpBank0();
	SD_Connect();
	MainMenu();
	FAT_Begin(fsp);	
    
	readDirectoryInfo(fsp, 0);
	
	contentsIndex = FileMenu(fsp);
	notePointer = loadSong(fsp, fs.dirContents[contentsIndex].clusterOffset, finalBuff, FINAL_BUFF_SIZE);


	ADC_init(&sar_handle);

   	
	fillPING(finalBuff[0]/1);
	
	////////////////// END INIT ///////////////////////////////////////////////
	
	/// Main Loop //
    while(1)
    {
    	updateButtons();
		if(push_lButton())
		{   
			set_dma0_ch0_stop();
    		set_dma0_ch1_stop();

			contentsIndex = FileMenu(fsp);
			currNote = 1;

			notePointer = loadSong(fsp, fs.dirContents[contentsIndex].clusterOffset, finalBuff, FINAL_BUFF_SIZE);
			fillPING(finalBuff[0]/1);
    		fillPONG(finalBuff[0]);

		}

        BlinkLED();
		currNote = GenerateAudioTone(currNote, notePointer);
		
		nowPlayingScr(&fs.dirContents[contentsIndex], currNote, 0);
	}

	return 0;
}

void playSong()
{
    while(1)
    {
    	updateButtons();
		if(push_lButton())
		{   
			//notePointer = loadSong(fsp, fs.dirContents[FileMenu(fsp)].clusterOffset, finalBuff, FINAL_BUFF_SIZE);
			//currNote = 0;
			return;
		}

        BlinkLED();
		//currNote = GenerateAudioTone(currNote, notePointer);
	}	
}
void setVolume(Int16 value )
{
	if(value > VOLUME_MAX)	
		value = VOLUME_MAX;
	else if(value < VOLUME_MIN)
		value = VOLUME_MIN;
		
	AIC3204_rset( 65, value); // Make a header / c file pair to fix this... move all the sound code there
	return;
}

void InitSystem(void)
{
	Uint16 i;
// PLL set up from RTC
    // bypass PLL
    CONFIG_MSW = 0x0;

    PLL_CNTL2 = 0x8000;
    PLL_CNTL4 = 0x0000;
    PLL_CNTL3 = 0x0806;
    PLL_CNTL1 = 0x82FA;
    
    while ( (PLL_CNTL3 & 0x0008) == 0);
    // Switch to PLL clk
    CONFIG_MSW = 0x1;

// clock gating
// enable all clocks
    IDLE_PCGCR = 0;
    IDLE_PCGCR_MSW = 0xFF84;
    
// reset peripherals
    PER_RSTCOUNT = 0x02;
    PER_RESET = 0x00fb;    
    for (i=0; i< 200; i++);
    
}

void ConfigPort(void)
{
    //  configure ports
    PERIPHSEL0 = 0x6900;        // parallel port: mode 6, serial port1: mode 2, serial port0: mode1 
}


void SYS_GlobalIntEnable(void)
{
    asm(" BIT (ST1, #ST1_INTM) = #0");
}

void SYS_GlobalIntDisable(void)
{
    asm(" BIT (ST1, #ST1_INTM) = #1");
}

Uint16 filterADC(Uint16 x0)
{
	Uint16 i, out;
	static Uint16 buf[FILTERLENGTH];
	Uint32 tout=0;
	buf[0] = x0;
	for(i = 1; i< FILTERLENGTH; i++)
		buf[i] = buf[i-1];
	for(i = 0; i < FILTERLENGTH; i++)
		tout += buf[i];
	tout = tout/FILTERLENGTH;
	out = (Uint16) tout;
	
	return out;
	
}
static toggleLED(void)
{
	Uint16 temp;

    temp = ST1_55;
    if((temp&0x2000) == 0)
    {
        // turn on LED
        temp |= 0x2000;
    }
    else
    {
        // turn off LED
        temp &=0xDFFF;
    }
    ST1_55 = temp; 

}


void BlinkLED(void)
{
   		if(Flag_RTC ==1)
		{
      		Flag_RTC =0;
			toggleLED();
		}
}

Uint16 GenerateAudioTone(Uint16 currNote, Uint16 noteAmount)
{
	
	static Uint16 Prev = 0;
	static Int16 read_input =0;
	Int16 new_input;
	Int16 override=0;

	
	
	ADC_read(&sar_handle, &adc_value);
	adc_value =  filterADC(adc_value);
	adc_value = 1024 - adc_value; 

	dynVolume = ((adc_value)/6) + VOLUME_MIN;
	
	setVolume(dynVolume);
	new_input = adc_value > triggerLevel;
	if(new_input)
	{
		if(read_input < DEBOUNCE)
  	  	{
			read_input++;
  		}
		else if(read_input >= DEBOUNCE)
		{
			read_input = DEBOUNCE*2;
		}
	}
	else
	{
		if(read_input > DEBOUNCE)
		{
			read_input--;
		}
		else if(read_input <= DEBOUNCE)
		{
			read_input = 0;
		}
	}
	override = 	push_rButton() || push_arButton();
	if((Prev != ((read_input > DEBOUNCE))) || override)
	{
		
		if((State == 0) && ((read_input > DEBOUNCE)|| override))
		{

	      //  set_dma0_ch0_stop();
	    //	set_dma0_ch1_stop();
	        //send_ping_ch1_i2s0();
		    send_ping_ch0_i2s0();
		  
			Prev = (read_input > DEBOUNCE) || override;
		    fillPONG(finalBuff[currNote] / 1);
		    currNote++;
		    State ++;
		    if(currNote >= noteAmount)
				currNote = 0;
			wait_usec(100);
		    
		}
		else if((State == 1) && ((read_input > DEBOUNCE)|| override))
		{

	      //  set_dma0_ch0_stop();
	    //	set_dma0_ch1_stop();
	    	
	       	//send_pong_ch1_i2s0();
	       	send_pong_ch0_i2s0();
		    
		    Prev = (read_input > DEBOUNCE);
		    fillPING(finalBuff[currNote] / 1);
		    currNote++;
		    State  = 0;
		    if(currNote >= noteAmount)
				currNote = 0;
			wait_usec(100);
		    
		}
		else
		{
			Prev = (read_input > DEBOUNCE);
			 set_dma0_ch0_stop();
    		set_dma0_ch1_stop();
		}
	}
	return currNote;
	
}


/////
// int nextFile(MF_FAT32_Filesystem *fs, int i)
// Find the first file in a position more than i in the current directory
//
int nextFile(MF_FAT32_Filesystem *fs, int i)
{
	i++;
   	while( i < 17 && (!(fs->dirContents[i].used == 1)))
   		i++;
   	if(i > 18)
   			i = 18;
	return i;
}



/////
// int prevFile(MF_FAT32_Filesystem *fs, int i)
// Find the first file in a position less than i in the current directory
//
int prevFile(MF_FAT32_Filesystem *fs, int i)
{
	i--;
   	while( i > 0 && (!(fs->dirContents[i].used == 1)))
   		i--;
   	if(i < -1)
   		i = -1;
	return i;
}


void dispFile(MF_FAT32_Filesystem *fs, int i, int l)
{
	LCD_setCursor(0, l);
    if(i < 17 && i > 0 && fs->dirContents[i].used == 1)
    {
    	if(fs->dirContents[i].tagTitle[0])
    	{
    		int len = strlen(fs->dirContents[i].tagTitle);
    		LCD_writeStringLen(fs->dirContents[i].tagTitle, (len>19)?19:len);
    	}
    	else
			LCD_writeString(fs->dirContents[i].name);
			
    }
    else if(i == 17 && (fs->dirContents[i].used))
    {
    	LCD_setCursor(0, l);
    	LCD_writeString(next_cluster);
    }
    else if(i == 0)
    {
    	if(current_cluster != 0)
    	{
    		LCD_setCursor(0, l);
    		LCD_writeString(prev_cluster);
    	}
    }
    return;
    	
}
void dispFiles(MF_FAT32_Filesystem *fs, int i)
{

	LCD_setCursor(0,0);
	LCD_writeString(currDir_str);
	LCD_writeString(fs->currDirectory.name);
	
   	dispFile(fs, nextFile(fs, i), 3);
  		
  		
    
    dispFile(fs, i, 2);
    if(fs->dirContents[i].used)
    {
		
		LCD_writeString(arrow_str);
    }
    
    
    dispFile(fs, prevFile(fs, i), 1);
	return;
}

int FileMenu(MF_FAT32_Filesystem *fs)
{
	static int i=1;
	LCD_sendCMD(CLEAR_LCD);
	dispFiles(fs,i);
	while(1)
	{

    	updateButtons();
   		if(push_rButton())
   		{
   			if(i == 17)
   			{
   				if(fs->dirContents[17].used == 1)
   				{
	   				current_cluster ++;
	   				readDirectoryInfo(fs, current_cluster);
	   				i=1;
	   				LCD_sendCMD(CLEAR_LCD);
					dispFiles(fs,i);
   				}
   			}
   			else if(i == 0 && current_cluster)
   			{
   				current_cluster --;
   				if(current_cluster < 0)
   					current_cluster = 0;
   				readDirectoryInfo(fs, current_cluster);
   				i=1;
   				LCD_sendCMD(CLEAR_LCD);
				dispFiles(fs,i);
   			}
			else if(fs->dirContents[i].used && i > 0 && i < 17)
			{
				nowPlayingScr(&fs->dirContents[i], 1, 1);
   				return i;
			}
   			else
   			{
   				LCD_setCursor(0, 0);
   				LCD_writeString(file_error);
   			}
   			
   		}
   		if(push_uButton())
   		{
   			LCD_sendCMD(CLEAR_LCD);
			i = prevFile(fs, i);
			dispFiles(fs,i);
   		}
   		if(push_dButton())
   		{
   			
			LCD_sendCMD(CLEAR_LCD);
			i =  nextFile(fs, i);
   			dispFiles(fs,i);
   		}
   		if(push_lButton())
   		{
   			MainMenu();
   			dispFiles(fs,i);
   		}
	}

	return 1;
}
void dispMenu(int i)
{
	LCD_setCursor(0,0);
	LCD_writeString("MF MUSIC MACHINE!");
	 LCD_setCursor(0, 1);
	LCD_writeString(main_menu[0]);
    if(i == 1)
    {
		LCD_writeString(arrow_str);
    }
    LCD_setCursor(0, 2);
	LCD_writeString(main_menu[1]);
	if(i == 2)
		LCD_writeString(arrow_str);
	LCD_setCursor(0, 3);
	LCD_writeString(main_menu[2]);
	if(i == 3)
		LCD_writeString(arrow_str);
    
    

	return;
}
int MainMenu()
{
	static int i=1;
	LCD_sendCMD(CLEAR_LCD);
	dispMenu(i);
	while(1)
	{

    	updateButtons();
   		if(push_rButton())
   		{
			switch(i)
			{
				case 0:
				break;
				case 1:
					return 1;
				break;
				case 2:
					
				break;
				case 3:
					settingsMenu();
					LCD_sendCMD(CLEAR_LCD);
					dispMenu(i);
				break;
			}
   			
   		}
   		if(push_uButton())
   		{
   			LCD_sendCMD(CLEAR_LCD);
   			if(i != 1)
				i--;
			dispMenu(i);
   		}
   		if(push_dButton())
   		{
   			LCD_sendCMD(CLEAR_LCD);
   			if(i != 3)
				i++;
			dispMenu(i);
   		}
	}
}
void dispSettings(int i)
{
	char buf[5];
	int key;
	LCD_setCursor(0,0);
	LCD_writeString(set_menu[0]);
	if(i == 0)
    {
		LCD_writeString(arrow_str);
    }
	 LCD_setCursor(0, 1);
	LCD_writeString(set_menu[1]);
	buf[0] = (int)(get_Tuning())%10 + '0';
	buf[1] = '.';
	buf[2] = (int)(get_Tuning()*10)%10 + '0';
	buf[3] = (int)(get_Tuning()*100)%10 + '0';
	buf[4] = 0;
	
	LCD_writeString(buf);
    if(i == 1)
    {
		LCD_writeString(arrow_str);
    }
    LCD_setCursor(0, 2);
	LCD_writeString(set_menu[2]);
	key = get_Key();
	if(key < 0)
	{
		key = -key;
		buf[0] ='-';
	}
	else
		buf[0] ='+';
	buf[1] = (key/100)%10 + '0';
	buf[2] = (key/10)%10 + '0';
	buf[3] = (key)%10 + '0';
	buf[4] = 0;
	LCD_writeString(buf);
	if(i == 2)
		LCD_writeString(arrow_str);
	LCD_setCursor(0, 3);
	LCD_writeString(set_menu[3]);
	buf[0] = (triggerLevel/100)%10 + '0';
	buf[1] = (triggerLevel/10)%10 + '0';
	buf[2] = (triggerLevel)%10 + '0';
	buf[3] = 0;
	LCD_writeString(buf);
	if(i == 3)
		LCD_writeString(arrow_str);
    
    

	return;
}
int settingsMenu()
{
	static int i=1;
	int done = 0;
	
	LCD_sendCMD(CLEAR_LCD);
	dispSettings(i);
	while(!done)
	{

    	updateButtons();
   		if(push_rButton())
   		{
			switch(i)
			{
				case 0:
					done = 1;
				break;
				case 1:
					set_Tuning(get_Tuning() + 0.01);
				break;
				case 2:
					set_Key(get_Key() + 1);
				break;
				case 3:
					triggerLevel += 10;
				break;
				default:
				break;
			}
   			LCD_sendCMD(CLEAR_LCD);
   			dispSettings(i);
   		}
   		if(push_lButton())
   		{
			switch(i)
			{
				case 0:
					done = 1;
				break;
				case 1:
					set_Tuning(get_Tuning() - 0.01);
				break;
				case 2:
					set_Key(get_Key() - 1);
				break;
				case 3:
					triggerLevel -= 10;
				break;
				default:
				break;
			}
			LCD_sendCMD(CLEAR_LCD);
   			dispSettings(i);
   		}
   		if(push_uButton())
   		{
   			LCD_sendCMD(CLEAR_LCD);
   			if(i != 0)
				i--;
			dispSettings(i);
   		}
   		if(push_dButton())
   		{
   			LCD_sendCMD(CLEAR_LCD);
   			if(i != 3)
				i++;
			dispSettings(i);
   		}
	}
	return 0;
}

void nowPlayingScr(MF_FileInfo *theFile, int playedNote, int override)
{
	int len;
	char buf[4];

	const char UnknownAuthor[8] = "Unknown\0";
	const int scrollSpeed = 200;
	static int wrapLoc = 0;
	static int prevNote = -1;
	char dupeName[103];
	
	wrapLoc++;
	
	if(override)
	{
		LCD_sendCMD(CLEAR_LCD);
		
		LCD_setCursor(0, 0);
		LCD_writeStringLen(nowPlaying, 12);
	
		LCD_setCursor(0, 1);
		LCD_writeStringLen(titleStr, 7);
	
		LCD_setCursor(0, 2);
		LCD_writeStringLen(authorStr, 7);	
		
		LCD_setCursor(0, 3);
		LCD_writeStringLen(noteStr, 14);
	}
	
	if(!(wrapLoc % scrollSpeed) || override)
	{

	LCD_setCursor(7, 1);
	len = strlen(theFile->tagTitle);
	
	if(len > 0)
	{
	strncpy(dupeName, theFile->tagTitle, len);
	dupeName[len] = ' ';
	dupeName[len + 1] = '-';
	dupeName[len + 2] = ' ';
	strncpy(&(dupeName[len + 3]), theFile->tagTitle, len);
	}
	else
	{
		len = strlen(theFile->name);
		strncpy(dupeName, theFile->name, len);
	dupeName[len] = ' ';
	dupeName[len + 1] = '-';
	dupeName[len + 2] = ' ';
	strncpy(&(dupeName[len + 3]), theFile->name, len);
	}
	
	if(wrapLoc / scrollSpeed >= len)
		wrapLoc = 0;
	
	LCD_writeStringLen(len>13?(&(dupeName[wrapLoc/scrollSpeed])):dupeName, len>=13?13:len);
	
	LCD_setCursor(7, 2);
	len = strlen(theFile->tagAuthor);
	
	if(len > 0)
	{
	strncpy(dupeName, theFile->tagAuthor, len);
	dupeName[len] = ' ';
	dupeName[len + 1] = '-';
	dupeName[len + 2] = ' ';
	strncpy(&(dupeName[len + 3]), theFile->tagAuthor, len);
	}
	else
	{
		len = strlen(UnknownAuthor);
		strncpy(dupeName, UnknownAuthor, len);
	dupeName[len] = ' ';
	dupeName[len + 1] = '-';
	dupeName[len + 2] = ' ';
	strncpy(&(dupeName[len + 3]), UnknownAuthor, len);
	}
	
	
	
	LCD_writeStringLen(dupeName, len>=13?13:len);
	
	}
	
	if((playedNote != prevNote) || override)
	{
		prevNote = playedNote;
	
	LCD_setCursor(14, 3);
	buf[0] = (playedNote/1000)%10 + '0';
	buf[1] = (playedNote/100)%10 + '0';
	buf[2] = (playedNote/10)%10 + '0';
	buf[3] = (playedNote)%10 + '0';
	LCD_writeStringLen(buf, 4);
	}

	return;
}


